package com.sky.aop;

import com.sky.annotation.AutoFill;
import com.sky.context.BaseContext;
import com.sky.enumeration.OperationType;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.ObjectUtils;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.stereotype.Component;

import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.time.LocalDateTime;
import java.util.Arrays;
import java.util.Map;
import java.util.function.Function;
import java.util.stream.Collectors;

import static com.sky.enumeration.OperationType.INSERT;

/**
 * 属性自动填充切面
 */
@Aspect
@Component
@Slf4j
public class AutoFillAspect {
    //1、定义切入点表达式
    @Pointcut("execution(* com.sky.mapper.*.*(..)) && @annotation(com.sky.annotation.AutoFill)")
    public void doAspect() {
    }

    //前置、后置、返回后、异常、环绕
    //2、定义【前置】通知
    @Before("doAspect() && @annotation(autoFill)")
    public void before(JoinPoint joinPoint, AutoFill autoFill) throws NoSuchFieldException, IllegalAccessException, NoSuchMethodException, InvocationTargetException {
        //1、获取目标方法上待填充的对象
        Object[] args = joinPoint.getArgs();
        if (ObjectUtils.isEmpty(args)) {
            return;
        }
        Object entity = args[0];

        //2、获得注解（获取目前的操作类型：INSERT\UPDATE）
        //方式一
        /*MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature();
        OperationType operationType = methodSignature.getMethod().getAnnotation(AutoFill.class).value();*/

        //方式二
        OperationType operationType = autoFill.value();

        //3、根据操作类型去为对象填充属性（反射）
        Class<?> aClass = entity.getClass();
        //新增操作（填充四个字段）
        //预先将本类中所有私有字段对象全部存放在Map中（key:字段名称  value:Field字段对象）
        Map<String, Field> fieldMap = Arrays.stream(aClass.getDeclaredFields()).collect(Collectors.toMap(f -> f.getName(), Function.identity()));
        if (INSERT == operationType) {
            //通过字段反射为属性赋值
            fillField(entity, fieldMap, "createTime");

            //通过方法反射为属性赋值
            /*final Method createTimeMethod = aClass.getDeclaredMethod("setCreateTime", LocalDateTime.class);
            createTimeMethod.invoke(entity,LocalDateTime.now());*/

            fillField(entity, fieldMap, "updateTime");

            fillField(entity, fieldMap, "createUser");

            fillField(entity, fieldMap, "updateUser");
        } else {
            //修改操作（填充两个字段）
            fillField(entity, fieldMap, "updateTime");

            fillField(entity, fieldMap, "updateUser1");
        }
    }

    /**
     * @param entity    待填充对象
     * @param fieldName 待填充属性名称
     * @throws NoSuchFieldException
     * @throws IllegalAccessException
     */
    private void fillField(Object entity, Map<String, Field> fieldMap, String fieldName) throws NoSuchFieldException, IllegalAccessException {
        Field field = fieldMap.get(fieldName);
        if (field == null) {
            return;
        }
        field.setAccessible(true);
        if (field.getType() == LocalDateTime.class) {
            field.set(entity, LocalDateTime.now());
        } else {
            field.set(entity, BaseContext.getCurrentId());
        }
    }
}
